﻿
namespace Patterns4Net.Tests.PatternEnforcer.Core.Patterns
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Linq;
    using NUnit.Framework;
    using Patterns4Net.PatternEnforcer.Core;
    using Patterns4Net.PatternEnforcer.Core.Patterns;

    [TestFixture]
    public class PatternCheckersCompositionTest
    {
        public static Type GetPatternType(Type type)
        {
            var patternCheckerBase = GetBaseTypes(type).First(IsPatternChecker);
            return patternCheckerBase.GetGenericArguments()[0];
        }

        public static bool IsPatternChecker(Type memberType)
        {
            return memberType.IsAssignableFrom(typeof(IPatternChecker)) &&
                memberType.IsGenericType &&
                memberType.MakeGenericType() != typeof(PatternCheckerBase<>);
        }

        public static IEnumerable<Type> GetBaseTypes(Type type)
        {
            var current = type;
            while (current.BaseType != null && current.BaseType.FullName != "System.Object")
            {
                yield return current.BaseType;
                current = current.BaseType;
            }
        }

        [Test]
        public void AllTypePatternCheckersAreRegistered()
        {
            CompositionContainer container = this.GetContainer();
            var imports = container.GetExportedValue<Imports>();

            var expectedCheckers =
                from type in typeof(PatternEnforcer).Assembly.GetTypes()
                where type.IsGenericType && type.GetGenericTypeDefinition() != typeof(FluentPatternChecker<>)
                where GetBaseTypes(type).Any(IsPatternChecker)
                where type != typeof(WrapperChecker<>)
                select new { CheckerType = type, PatternType = GetPatternType(type) };

            var actualCheckers =
                from x in imports.Checkers
                select new { CheckerType = x.Value.GetType(), PatternType = x.Metadata.PatternType };

            foreach (var expectedChecker in expectedCheckers)
            {
                var message = string.Format(
                    "Type Pattern Checker of type {0} was not resolved by the composition container.", 
                    expectedChecker.CheckerType);
                Assert.IsTrue(actualCheckers.Contains(expectedChecker), message);
            }
        }

        private CompositionContainer GetContainer()
        {
            var mefCatalog = new AggregateCatalog(
                new AssemblyCatalog(typeof(PatternEnforcer).Assembly),
                new AssemblyCatalog(this.GetType().Assembly));
            return new CompositionContainer(mefCatalog);
        }

        [Export]
        public class Imports
        {
            [ImportingConstructor]
            public Imports(
                [ImportMany]Lazy<IPatternChecker, PatternCheckerExportAttribute>[] checkers)
            {
                this.Checkers = checkers;
            }

            public Lazy<IPatternChecker, PatternCheckerExportAttribute>[] Checkers { get; set; }
        }
    }
}
